﻿
Imports System.IO
Imports System.Text
Imports std
Imports streamhandle

Namespace streamhandle
    Public Module streamhandle3
        Public Function create_buff() As bytes
            Dim buff As bytes = Nothing
            buff = New bytes()
            buff.size() = readstep
            Return buff
        End Function

        Public Function read_begin(ByVal s As Stream,
                                   ByVal length As Int64,
                                   ByVal readcount As Int64,
                                   ByRef buff As bytes,
                                   ByVal ac As AsyncCallback,
                                   ByVal obj As Object) As IAsyncResult
            If Not stream_canread(s) Then
                Return Nothing
            End If

            Dim expect As Int64
            If length = npos Then
                expect = readstep
            Else
                expect = min(length - readcount, readstep)
            End If
            assert(expect > 0)
            assert(Not buff Is Nothing AndAlso buff.size() >= expect)
            Return s.BeginRead(+buff, 0, expect, ac, obj)
        End Function

        Public Sub read_end(ByVal ar As IAsyncResult, ByRef lasttime As Int64, ByVal s As Stream)
            Try
                lasttime = s.EndRead(ar)
            Catch
                lasttime = 0
            End Try
        End Sub

        Public Function write_begin(ByVal s As Stream,
                                    ByVal d() As Byte,
                                    ByVal length As Int64,
                                    ByVal ac As AsyncCallback,
                                    ByVal obj As Object) As IAsyncResult
            If Not stream_canwrite(s) Then
                Return Nothing
            Else
                Return s.BeginWrite(d, 0, length, ac, obj)
            End If
        End Function

        Public Sub write_end(ByVal s As Stream, ByVal ar As IAsyncResult)
            s.EndWrite(ar)
        End Sub
    End Module
End Namespace

Public Module _streamhandle3
    Private ReadOnly STREAMHANDLE3_READ_TICKS As Int64
    Private ReadOnly STREAMHANDLE3_READ_STEP_TICKS As Int64
    Private ReadOnly STREAMHANDLE3_WRITE_TICKS As Int64
    Private ReadOnly STREAMHANDLE3_TRAVACTION_TICKS As Int64

    Sub New()
        STREAMHANDLE3_READ_TICKS =
            counter.registerAverageAndLastAverage("STREAMHANDLE3_READ_TICKS")
        STREAMHANDLE3_READ_STEP_TICKS =
            counter.registerAverageAndLastAverage("STREAMHANDLE3_READ_STEP_TICKS")
        STREAMHANDLE3_WRITE_TICKS =
            counter.registerAverageAndLastAverage("STREAMHANDLE3_WRITE_TICKS")
        STREAMHANDLE3_TRAVACTION_TICKS =
            counter.registerAverageAndLastAverage("STREAMHANDLE3_TRAVACTION_TICKS")
    End Sub

    Public Delegate Function trav_f(ByVal d() As Byte, ByVal count As Int64, ByVal holdticks As Int64) As callbackAction
    Public Function traversalstream3(ByVal s As Stream, ByVal trav As trav_f,
                                     Optional ByVal offsetInStream As Int64 = npos,
                                     Optional ByVal length As Int64 = npos,
                                     Optional ByVal starttime As Int64 = npos,
                                     Optional ByVal holdtime As Int32 = maxInt32,
                                     Optional ByRef action As callbackAction = Nothing,
                                     Optional ByVal result As pointer(Of Int64) = Nothing) As Boolean
        'special treatment for length = 0
        If initial_traversal(s, offsetInStream, length, starttime, holdtime).false_() Then
            Return False
        End If
        If trav Is Nothing Then
            raiseError("no valid read_f defined.", errorHandle.errorType.warning)
            Return False
        End If

        If length = 0 Then
            action = callbackAction.finished_success()
        Else
            Dim buff As bytes = Nothing
            buff = create_buff()
            Dim readcount As Int64 = 0
            Dim lasttime As Int64 = npos
            Dim travAction As callbackAction = Nothing
            Dim readAction As callbackAction = Nothing
            readAction = New callbackActionAsyncOperation(
                                Function(ByRef ac As AsyncCallback,
                                         ByRef obj As Object) As IAsyncResult
                                    assert(travAction Is Nothing OrElse
                                           travAction.finished())
                                    Return read_begin(s, length, readcount, buff, ac, obj)
                                End Function,
                                Sub(ByRef ar As IAsyncResult)
                                    read_end(ar, lasttime, s)
                                    counter.recordTimeTicks(STREAMHANDLE3_READ_STEP_TICKS,
                                                            readAction.begin_ticks())
                                End Sub,
                                millisecondsToTicks(timeoutms(starttime, holdtime)))

            Const reading As UInt32 = stepCallbackAction.initial_step
            Const traving As UInt32 = reading + 1
            action = New stepCallbackAction(
                            Function(ByRef [step] As UInt32) As Boolean
                                Select Case [step]
                                    Case reading
                                        assert(readAction.begining())
                                        Return begin(readAction)
                                    Case traving
                                        assert(lasttime > 0)
                                        travAction =
                                            trav(buff, lasttime,
                                                 millisecondsToTicks(timeoutms(starttime, holdtime)))
                                        readcount += lasttime
                                        Return begin(travAction)
                                    Case Else
                                        Return assert(False)
                                End Select
                            End Function,
                            Function(ByRef [step] As UInt32) As Boolean
                                Select Case [step]
                                    Case reading
                                        assert(travAction Is Nothing OrElse
                                               callbackAction.success_finished(travAction))
                                        Return stepCallbackAction.action_check(
                                                    readAction,
                                                    Sub(ByRef _step As UInt32)
                                                        _step = stepCallbackAction.gotoStepIf(lasttime > 0, traving)
                                                    End Sub,
                                                    [step])
                                    Case traving
                                        assert(Not travAction Is Nothing)
                                        Return stepCallbackAction.action_check(
                                                    travAction,
                                                    Sub(ByRef _step As UInt32)
                                                        counter.recordTimeTicks(STREAMHANDLE3_TRAVACTION_TICKS,
                                                                                travAction.begin_ticks())
                                                        If (length > 0 AndAlso readcount < length) OrElse
                                                           (length = npos AndAlso lasttime > 0) Then
                                                            _step = reading
                                                            readAction.reset(
                                                                millisecondsToTicks(timeoutms(starttime, holdtime)))
                                                        Else
                                                            _step = stepCallbackAction.finished_step
                                                        End If
                                                    End Sub,
                                                    [step])
                                    Case Else
                                        Return assert(False)
                                End Select
                            End Function,
                            Function() As Boolean
                                If Not result Is Nothing Then
                                    result.setinstance(readcount)
                                End If
                                counter.recordTimeTicks(STREAMHANDLE3_READ_TICKS,
                                                        callbackAction.current().begin_ticks())
                                buff.clear()
                                Return True
                            End Function,
                            millisecondsToTicks(timeoutms(starttime, holdtime)))
        End If
        Return begin(action)
    End Function

    Private Function writestreamAction(ByVal o As Stream, ByVal d() As Byte,
                                       Optional ByVal length As Int64 = npos,
                                       Optional ByVal starttime As Int64 = npos,
                                       Optional ByVal holdtime As Int32 = maxInt32,
                                       Optional ByRef action As callbackAction = Nothing) As Boolean
        Dim rtn As ternary = Nothing
        rtn = initial_write(o, d, 0, length, starttime, holdtime)
        If rtn.notunknown() Then
            Return rtn.true_()
        End If

        action = New callbackActionAsyncOperation(Function(ByRef ac As AsyncCallback,
                                                           ByRef obj As Object) As IAsyncResult
                                                      Return write_begin(o, d, length, ac, obj)
                                                  End Function,
                                                  Sub(ByRef ar As IAsyncResult)
                                                      write_end(o, ar)
                                                      counter.recordTimeTicks(STREAMHANDLE3_WRITE_TICKS,
                                                                              callbackAction.current().begin_ticks())
                                                  End Sub,
                                                  millisecondsToTicks(timeoutms(starttime, holdtime)))
        Return True
    End Function

    Public Function writestream3(ByVal o As Stream, ByVal d() As Byte,
                                 Optional ByVal length As Int64 = npos,
                                 Optional ByVal starttime As Int64 = npos,
                                 Optional ByVal holdtime As Int32 = maxInt32,
                                 Optional ByRef action As callbackAction = Nothing) As Boolean
        If writestreamAction(o, d, length, starttime, holdtime, action) Then
            If action Is Nothing Then
                Return True
            Else
                Return begin(action)
            End If
        Else
            Return False
        End If
    End Function

    Private Class syncstreamHandler
        Private o As Stream

        Public Sub New(ByVal s As Stream, ByVal holdtime As Int32)
            o = s
            Try
                o.WriteTimeout = holdtime
            Catch
            End Try
        End Sub

        Public Function write(ByVal d() As Byte, ByVal count As Int64, ByVal holdticks As Int64) As callbackAction
            assert(count > 0)
            Dim rtn As callbackAction = Nothing
            If writestreamAction(o, d, count, npos, ticksToMilliseconds(holdticks), rtn) Then
                Return rtn
            Else
                Return Nothing
            End If
        End Function
    End Class

    Public Function syncstream3(ByVal i As Stream, ByVal o As Stream,
                                Optional ByVal offsetInStream As Int64 = npos,
                                Optional ByVal length As Int64 = npos,
                                Optional ByVal starttime As Int64 = npos,
                                Optional ByVal holdtime As Int32 = maxInt32,
                                Optional ByRef action As callbackAction = Nothing,
                                Optional ByVal result As pointer(Of Int64) = Nothing) As Boolean
        If o Is Nothing OrElse Not o.CanWrite Then
            raiseError("cannot write to output stream.", errorHandle.errorType.warning)
            Return False
        End If

        Dim handler As syncstreamHandler = Nothing
        handler = New syncstreamHandler(o, holdtime)
        Return traversalstream3(i, AddressOf handler.write, offsetInStream, length, starttime, holdtime, action, result)
    End Function

    Private Function _readstream(ByVal i As Stream,
                                 ByVal offsetInStream As Int64,
                                 ByVal length As Int64,
                                 ByVal starttime As Int64,
                                 ByVal holdtime As Int32,
                                 ByRef action As callbackAction,
                                 ByVal writer As trav_f,
                                 ByVal finish As void) As Boolean
        assert(Not writer Is Nothing)
        assert(Not finish Is Nothing)
        Dim trav As callbackAction = Nothing
        action = New callbackAction(Function() As Boolean
                                        Return traversalstream3(i, writer, offsetInStream, length,
                                                                starttime, holdtime, trav)
                                    End Function,
                                    callbackAction.action_check(Function() As callbackAction
                                                                    Return trav
                                                                End Function),
                                    Function() As Boolean
                                        If callbackAction.success_finished(trav) Then
                                            finish()
                                            Return True
                                        Else
                                            Return False
                                        End If
                                    End Function,
                                    millisecondsToTicks(timeoutms(starttime, holdtime)))
        Return begin(action)
    End Function

    Private Class readstreamHandler
        Private i As vector(Of bytes) = Nothing

        Public Sub New()
            i = New vector(Of bytes)()
        End Sub

        Public Function write(ByVal d() As Byte, ByVal count As Int64, ByVal holdticks As Int64) As callbackAction
            i.push_back(New bytes(d, 0, count))
            Return callbackAction.finished_success()
        End Function

        Public Function output() As vector(Of bytes)
            Return i
        End Function
    End Class

    Public Function readstream3(ByVal i As Stream,
                                Optional ByVal offsetInStream As Int64 = npos,
                                Optional ByVal length As Int64 = npos,
                                Optional ByVal starttime As Int64 = npos,
                                Optional ByVal holdtime As Int32 = maxInt32,
                                Optional ByRef action As callbackAction = Nothing,
                                Optional ByVal result As pointer(Of bytes) = Nothing) As Boolean
        Dim handler As readstreamHandler = Nothing
        handler = New readstreamHandler()
        Return _readstream(i, offsetInStream, length, starttime, holdtime, action, AddressOf handler.write,
                           Sub()
                               If Not result Is Nothing Then
                                   Dim bt As bytes = Nothing
                                   Dim len As Int64 = 0
                                   For j As Int64 = 0 To handler.output().size() - 1
                                       len += handler.output()(j).size()
                                   Next
                                   bt = New bytes()
                                   bt.size() = len
                                   len = 0
                                   For j As Int64 = 0 To handler.output().size() - 1
                                       memcpy(+bt, len,
                                              +(handler.output()(j)), 0,
                                              handler.output()(j).size())
                                       len += handler.output()(j).size()
                                   Next
                                   result.setinstance(bt)
                               End If
                           End Sub)
    End Function

    Private Class readstreamstringHandler
        Private i As StringBuilder = Nothing
        Private decoder As Decoder = Nothing

        Public Sub New(ByVal e As Text.Encoding)
            i = New StringBuilder()
            If e Is Nothing Then
                e = encoding.utf8()
            End If
            decoder = e.GetDecoder()
        End Sub

        Public Function write(ByVal d() As Byte, ByVal count As Int64, ByVal holdticks As Int64) As callbackAction
            Dim cs() As Char = Nothing
            Dim len As Int32 = 0
            ReDim cs(decoder.GetCharCount(d, 0, count) - 1)
            len = decoder.GetChars(d, 0, count, cs, 0)
            i.Append(cs, 0, len)
            Return callbackAction.finished_success()
        End Function

        Public Function output() As String
            Return Convert.ToString(i)
        End Function
    End Class

    Public Function readstreamstring3(ByVal i As Stream,
                                      Optional ByVal offsetInStream As Int64 = npos,
                                      Optional ByVal length As Int64 = npos,
                                      Optional ByVal starttime As Int64 = npos,
                                      Optional ByVal holdtime As Int32 = maxInt32,
                                      Optional ByVal e As Text.Encoding = Nothing,
                                      Optional ByRef action As callbackAction = Nothing,
                                      Optional ByVal result As pointer(Of String) = Nothing) As Boolean
        Dim handler As readstreamstringHandler = Nothing
        handler = New readstreamstringHandler(e)
        Return _readstream(i, offsetInStream, length, starttime, holdtime, action, AddressOf handler.write,
                           Sub()
                               If Not result Is Nothing Then
                                   result.set(handler.output())
                               End If
                           End Sub)
    End Function
End Module
